#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <pthread.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

namespace Client
{
    using namespace std;

    enum
    {
        USAGE_ERR = 1,
        SOCK_ERR,
    };

    class udpClient
    {
    public:
        udpClient(const string &serverip, const uint16_t &serverport)
            : _sockfd(-1), _serverip(serverip), _serverport(serverport), _quit(false)
        {
        }

        ~udpClient()
        {
        }

        void initClient()
        {
            // _sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
            _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            if (_sockfd == -1)
            {
                cerr << "socket error: " << errno << " -> " << strerror(errno) << endl;
                exit(SOCK_ERR);
            }
            cout << "socket success : " << _sockfd << endl;

            // client需不需要bind？[必须要的！]
            // 但是需不需要显式地bind呢，需不需要程序员自己bind呢？[不需要！]
            // 我们的主机跑着很多程序，如果程序员自己绑了指定端口号，万一这个端口号被占用了，这个客户端就起不来了
            // 所以由OS自动形成端口进行bind，包括IP地址
            // 那是什么时候bind的呢？
        }

        static void *readerMsg(void *args)
        {
            int sockfd = *(static_cast<int *>(args));

            while (true)
            {
                char response[1024];
                struct sockaddr_in server;
                memset(&server, 0, sizeof(server));
                socklen_t len = sizeof(server);
                ssize_t n = recvfrom(sockfd, response, sizeof(response) - 1, 0, (struct sockaddr *)&server, &len);
                if (n >= 0)
                    response[n] = 0; // 这里n为什么要等于0？因为要清除缓冲区，上面的栈帧结构很稳定了，response这个变量位置还可能存着上次的内容
                cout << response << endl;
            }
        }

        void run()
        {
            cout << "client started !" << endl;

            pthread_create(&_reader, nullptr, readerMsg, (void *)&_sockfd);

            struct sockaddr_in peer;
            memset(&peer, 0, sizeof(peer));
            peer.sin_family = AF_INET;
            peer.sin_addr.s_addr = inet_addr(_serverip.c_str());
            peer.sin_port = htons(_serverport);

            string msg;
            while (!_quit)
            {
                // 主线程->send
                cerr << "Please Enter# ";
                getline(cin, msg);

                sendto(_sockfd, msg.c_str(), msg.size(), 0, (struct sockaddr *)&peer, sizeof(peer));
                // 回答上面什么时候bind的问题，在我们首次send的时候bind的！
            }
        }

    private:
        int _sockfd;
        string _serverip;
        uint16_t _serverport;
        bool _quit;
        pthread_t _reader;
    };
}